﻿using System;
using System.Linq;

using Microsoft.WowAddonStudio.Lua.AST;
using Microsoft.WowAddonStudio.Lua.Model;
using Microsoft.WowAddonStudio.Lua.Parser;

using NUnit.Framework;

namespace Microsoft.WowAddonStudio.Lua.Tests.Parser
{
    [TestFixture]
    public class DocumentationParserTest : ParserTestBase<DocumentationParser>
    {
        private DocumentationScope globalScope;

        public override void SetUp()
        {
            // Initialize global scope for the test
            globalScope = new DocumentationScope();

            // Create the parser
            parser = new DocumentationParser(globalScope)
                     {
                         Tracing = true,
                     };
        }

        [Test]
        public void ChunkScopeTest()
        {
            // Set the source to an empty file
            parser.SetSource(String.Empty);

            // Assert the parser
            this.AssertParse();

            // Assert that the chunk parsed has a scope that is the child of the global scope
            Assert.AreSame(globalScope, parser.Chunk.Scope.Parent);
            Assert.AreSame(parser.Scope, parser.Chunk.Scope);
        }

        [Test]
        public void AssignmentTest()
        {
            // Set the source to a single assignment
            parser.SetSource("x = 5");

            // Assert the parse
            this.AssertParse();

            // Assert declaration in global scope
            Assert.AreEqual(1, globalScope.Declarations.Count());
            Assert.AreEqual(LuaType.Number, globalScope.Resolve<Declaration>("x").Type);
        }

        [Test]
        public void LocalAssignmentTest()
        {
            // Set the source to a single local assignment
            parser.SetSource("local x = 5");

            // Assert the parse
            this.AssertParse();

            // Assert declaration in parser scope
            Assert.AreEqual(0, globalScope.Declarations.Count());
            Assert.AreEqual(1, parser.Scope.Declarations.Count());
            Assert.AreEqual(LuaType.Number, parser.Scope.Resolve<Declaration>("x").Type);
        }

        [Test]
        public void FunctionParameterTest()
        {
            // Set the source to a function declaration with a single parameter
            parser.SetSource("function foo(x) print(x) end");

            // Assert the parse
            this.AssertParse();

            // Assert that foo is declared in the global scope and x is declared in the table
            var functionDeclaration = (FunctionDeclaration)parser.Chunk.Block.StatementList;
            var functionScope = (DocumentationScope)functionDeclaration.Body.Scope;

            Assert.AreEqual(1, globalScope.Declarations.Count());
            Assert.AreEqual(2, functionScope.Declarations.Count());

            Assert.IsNull(globalScope.Resolve<Declaration>("x"));
            Assert.IsNotNull(globalScope.Resolve<Declaration>("foo"));
            Assert.IsNotNull(functionScope.Resolve<Declaration>("x"));
        }

        [Test]
        public void TableConstructorTest()
        {
            // Set the source to a function declaration with a single parameter
            parser.SetSource("foo = { bar = 5 }");

            // Assert the parse
            this.AssertParse();

            // Assert that foo is declared in the global scope and bar is declared in the table
            Assert.AreEqual(1, globalScope.Declarations.Count());

            Assert.IsNull(globalScope.Resolve<Declaration>("bar"));
            Assert.IsNotNull(globalScope.Resolve<Declaration>("foo"));
            Assert.IsNotNull(globalScope.Resolve<Declaration>("foo.bar"));
        }

        [Test]
        public void QualifiedAssignmentTest()
        {
            // Set the source to a table declaration and a qualified assignment
            parser.SetSource("foo = {}; foo.bar = 5");

            // Assert the parse
            this.AssertParse();

            // Assert that foo is declared in the global scope and bar is declared in the table
            Assert.AreEqual(1, globalScope.Declarations.Count());

            Assert.IsNull(globalScope.Resolve<Declaration>("bar"));
            Assert.IsNotNull(globalScope.Resolve<Declaration>("foo"));
            Assert.IsNotNull(globalScope.Resolve<Declaration>("foo.bar"));

            // TODO: Do not resolve method notation to non-function fields
            //Assert.IsNull(globalScope.Resolve<Declaration>("foo:bar"));
        }

        [Test]
        public void QualifiedFunctionDeclarationTest()
        {
            // Set the source to a table declaration and a qualified function declaration
            parser.SetSource("foo = {}; function foo:bar() end");

            // Assert the parse
            this.AssertParse();

            // Assert that foo is declared in the global scope and bar is declared in the table
            Assert.AreEqual(1, globalScope.Declarations.Count());

            Assert.IsNull(globalScope.Resolve<Declaration>("bar"));
            Assert.IsNotNull(globalScope.Resolve<Declaration>("foo"));
            Assert.IsNotNull(globalScope.Resolve<Declaration>("foo.bar"));
            Assert.IsNotNull(globalScope.Resolve<Declaration>("foo:bar"));

        }
    }
}